package com.nova.scenic.projectlibs.util;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import com.nova.scenic.projectlibs.util.collection.Collections;
import com.nova.scenic.projectlibs.util.debug.LogcatShowWindowManager;
import com.nova.scenic.projectlibs.util.debug.MyLog;
import com.nova.scenic.projectlibs.util.debug.ObjectUtil;
import com.nova.scenic.projectlibs.util.fragment.FragmentOperator;
import com.scenic.rd_android.R;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Random;

//import android.os.SystemProperties;

/**
 * 不放到debug包下，因为需要在BaseActivity 一个包下.
 * 运行过程只有一个实例，自己构造的会没有效果
 *
 * @author scenic
 */

public class DebugUtil {

    private static final String TAG = "DebugUtil";

    private static int mRandomColor[] = {Color.BLUE, Color.CYAN, Color.DKGRAY, Color.GREEN, Color.LTGRAY, Color.RED, Color.YELLOW,};

    private static DebugUtil mInstance;
    private static Random mRandom = new Random();

    private WeakReference<Context> mContext;
    private int cmdIndex = 0;
    private String[] allInputCmd;

    private DebugUtil() {

    }

    public static int getRandomColor() {
        return mRandomColor[mRandom.nextInt(mRandomColor.length)];
    }

    public int nextCmdValueAsInt() {
        int value = 0;
        String number = null;
        try {
            number = nextCmdValue();
            value = Integer.parseInt(number);
        } catch (NumberFormatException e) {
            print("NumberFormat Error " + number);
            e.printStackTrace();
        }
        return value;
    }

    public String nextCmdValue() {
        if (allInputCmd == null) {
            return "";
        }
        String temp;
        try {
            temp = allInputCmd[cmdIndex];
            cmdIndex++;
        } catch (Exception e) {
            return null;
        }
        if (temp == null) {
            return "";
        }
        return temp;
    }

    public static DebugUtil getInstance() {
        //create instance
        if (mInstance == null) {
            mInstance = new DebugUtil();
        }

        return mInstance;
    }

    public void updateContext(Context context) {
        if (mContext == null) {
            mContext = new WeakReference<>(context);
        } else if (!mContext.get().equals(context)) {
            mContext.clear();
            mContext = new WeakReference<>(context);
        } else {
            MyLog.i(TAG, "current context " + MyLog.getInstanceName(mContext.get()) + " new context " + MyLog.getInstanceName(context));
        }
    }

    private Context getContext() {
        return mContext.get();
    }

    private HashMap<String, ViewGroup> mViewGroups = new HashMap<String, ViewGroup>();
    private View mCurrentView = null;
    private Writer mWrite = null;


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    void dump(String prefix, FileDescriptor fd, final PrintWriter writer, String[] args) {
        allInputCmd = args;
        mWrite = writer;

        try {
            String cmdType = nextCmdValue();

            //if want help
            if (cmdType.equals("help")) {
                showHelp(writer);
                return;
            }

            CmdType type;
            try {
                type = CmdType.valueOf(cmdType);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
            switch (type) {
                case debug_switch:
                    // open or close debug
                    String value = nextCmdValue();
                    if (value.equals("true")) {
                        BaseApplication.isInDebugMode = true;
                    } else {
                        BaseApplication.isInDebugMode = false;
                    }

//                    new SaveValue(mContext.get()).putString(Constant.Debug.KEY_DEBUG_SWITCH, value);
                    writer.append("current debug mode is " + BaseApplication.isInDebugMode + " \n").flush();
                    break;
                case getCurrentFocusView:
                    mCurrentView = initCurrentViewByCurrentFocusView();

                    if (mCurrentView != null) {
                        writer.append("current view is " + mCurrentView + " isEnable " + mCurrentView.isEnabled() + "  isFocusable " + mCurrentView.isFocusable() + "\n").flush();
                        writer.append("current view id is " + findRDeclaredNameByValue(mCurrentView.getId()) + "  " + mCurrentView.getId() + "\n").flush();
                    } else {
                        print("not view get focus");
                    }

//                    List<BaseDialogFragment> child = BaseDialogFragment.getAllInstance();
//                    if (child != null && child.size() > 0) {
//                        BaseDialogFragment tmp = child.get(child.size() - 1);
//                        tmp.debugFocus(tmp.getView());
//                    }

                    break;
                case getFocusViewNextFocusId:
                    mCurrentView = initCurrentViewByCurrentFocusView();
                    print("focus view is " + mCurrentView + "  " + findRDeclaredNameByValue(mCurrentView.getId()));
                    print("next up id is " + mCurrentView.getNextFocusUpId());
                    print("next down id is " + mCurrentView.getNextFocusDownId());
                    print("next left id is " + mCurrentView.getNextFocusLeftId());
                    print("next right id is " + mCurrentView.getNextFocusRightId());
                    break;
                case debug_view_all:
                    String str = ObjectUtil.showObjectField(mCurrentView);
                    print(str);
                case debug_view_pick:
                    String log = ObjectUtil.showObjectField(mCurrentView, nextCmdValue());
                    print(log);
                    break;
                case debugViewHierarchy:
                    Class<?> currentClass = mCurrentView.getClass();
                    Class<?> viewClass = null;

                    String lookAtView = "android.view.View";
                    try {
                        lookAtView = nextCmdValue();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    do {
                        String className = currentClass.getName();
                        print(className);
                        if (className.equals(lookAtView)) {
                            viewClass = currentClass;
                        }
                    } while ((currentClass = currentClass.getSuperclass()) != null);

                    // log = ObjectUtil.showObjectField(viewClass, mCurrentView,
                    // null);
                    print("reflect view is " + viewClass);
                    // print(log);
                    break;
                case init_view_group:
                    if (mContext.get() instanceof Activity) {
                        mCurrentView = ((Activity) mContext.get()).getCurrentFocus();
                        if (mCurrentView != null) {
                            ViewGroup tempGroup = (ViewGroup) mCurrentView.getParent();
                            if (tempGroup != null) {
                                int index = 1;
                                mViewGroups.put(index + "", tempGroup);
                                print("key:1 " + tempGroup);

                                while (tempGroup != null) {
                                    tempGroup = (ViewGroup) tempGroup.getParent();
                                    mViewGroups.put(++index + "", tempGroup);
                                    print(index + ":" + tempGroup);
                                }
                            } else {
                                print("view group of " + mCurrentView + " is null");
                            }
                        } else {
                            Toast.makeText(getContext(), "mCurrentView is null ", Toast.LENGTH_LONG).show();
                        }
                    } else {
                        Toast.makeText(getContext(), "context not activity", Toast.LENGTH_LONG).show();
                    }
                    break;
                case save_value_put: {
                    String valueType = nextCmdValue();
                    print("value type is " + valueType);

                    if (valueType.equals("int")) {
                        new SaveValue(mContext.get()).putInt(nextCmdValue(), nextCmdValueAsInt());
                        print("save int value ok ");
                    }

                    if (valueType.equals("string")) {
                        new SaveValue(mContext.get()).putString(nextCmdValue(), nextCmdValue());
                        print("save String value ok ");
                    }

                }


                break;
                case save_value_get: {
                    String valuetype = nextCmdValue();
                    String key = nextCmdValue();
                    if (valuetype.equals("int")) {
                        print("the int value is " + new SaveValue(mContext.get()).getInt(key, -1));
                    }
                    if (valuetype.equals("string")) {
                        print("the int value is " + new SaveValue(mContext.get()).getString(key));
                    }
                }
                break;
                case save_value_remove:
                    new SaveValue(mContext.get()).remove(nextCmdValue());
                    break;
                case show_color_view:
                    dealShowColor();
                    break;

                case test:
                    break;

                case show_fragmentInfo:
//				print(SystemFragment.getInstance(mContext).getFragmentInfo());
                    break;
                case changeBrand:
//                    SystemProperties.set("ro.product.brand", nextCmdValue());
//                    print("ro.product.brand is " + SystemProperties.get("ro.product.brand"));
                    break;
                case hideIME:
                    InputMethodManager manager = (InputMethodManager) mContext.get().getSystemService(Context.INPUT_METHOD_SERVICE);
                    manager.toggleSoftInput(0, 0);
                    break;
                case isIMEShow:
                    break;
                case debug_channel_getChannelValue:
                    break;
                case debug_dtv_getRFChannel:
                    break;
                case debug_bluetooth:
                    break;
                case debug_clear_fota:
                    File recoveryFile = new File("/cache/recovery/update.zip");
                    recoveryFile.delete();
                    break;

                case getVersion:
                    PackageInfo info = mContext.get().getPackageManager().getPackageInfo(mContext.get().getPackageName(), 0);
                    print("versionCode " + info.versionCode + " verName " + info.versionName);
                    break;
                case fragment_execute_current:

                    ArrayList<android.app.Fragment> list = FragmentOperator.FragmentHelper.getFragmentOperator().getFragmentList();
//                    BaseFragment fragment =
//                            (BaseFragment) FragmentOperator.FragmentHelper.getFragmentOperator().
//                                    getCurrentFragmentFilterDialogFragment();
                    android.app.Fragment fragment = list.get(list.size() - 1);
                    if (fragment != null) {
//                        if (fragment instanceof BaseDialogFragment) {
//                            ((BaseDialogFragment) fragment).onDumpExecute(this, nextCmdValue());
//                        } else {
//                            fragment.dump(prefix, fd, writer, args);
//                        }
                    }

                    break;
                case execute_update:
//                    PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
//                    pm.setNextBootMode("factory2");
//                    pm.reboot("factory2"
                    break;
                case showLogcat:
                    if (mContext.get() instanceof Activity) {
                        new LogcatShowWindowManager((Activity) mContext.get()).showLogcatWindowManager();
                    }
                    break;

                case fragment_list_all:

                    int count = fragmentManager().getBackStackEntryCount();
                    print("count " + count);
                    for (int i = 0; i < count; i++) {
                        FragmentManager.BackStackEntry entity = fragmentManager().getBackStackEntryAt(i);
                        print(entity.getName() + " " + entity.getId());
                    }
//            ArrayList<Fragment> mActive;
//            ArrayList<Fragment> mAdded;
                    try {
                        Field mAddMethod = fragmentManager().getClass().getDeclaredField("mAdded");
                        mAddMethod.setAccessible(true);
                        ArrayList<Fragment> mAdd = (ArrayList<Fragment>) mAddMethod.get(fragmentManager());
                        print(mAdd.toString());
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                    try {
                        Field mAddMethod = fragmentManager().getClass().getDeclaredField("mActive");
                        mAddMethod.setAccessible(true);
                        ArrayList<Fragment> mActive = (ArrayList<Fragment>) mAddMethod.get(fragmentManager());
                        print(mActive.toString());
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    break;
                case window_show_dim:
                    if (mContext.get() instanceof Activity) {
                        float dimAmount = ((Activity) mContext.get()).getWindow().getAttributes().dimAmount;
                        print("dimAmount " + dimAmount);
                    }

                    break;
                case window_set_dim:
                    if (mContext.get() instanceof Activity) {
                        WindowManager.LayoutParams att = ((Activity) mContext.get()).getWindow().getAttributes();
                        att.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;

                        att.dimAmount = Float.valueOf(nextCmdValue());

                        ((Activity) mContext.get()).getWindow().setAttributes(att);
                    }

                    break;
                case getMemoryInfo:
                    long maxMemory = Runtime.getRuntime().maxMemory();
                    long freeMemory = Runtime.getRuntime().freeMemory();
                    long totalMemory = Runtime.getRuntime().totalMemory();
                    print("maxMemory is " + Formatter.formatFileSize(mContext.get(), maxMemory));
                    print("freeMemory is " + Formatter.formatFileSize(mContext.get(), freeMemory));
                    print("totalMemory is " + Formatter.formatFileSize(mContext.get(), totalMemory));
                    break;
                case custom:
                    externalCallbacks.notifyObservers(this);
                    break;
                default:
                    reset();
                    showHelp(writer);
                    break;
            }

        } catch (Exception e) {
            showHelp(writer);
            print(e.toString());
            e.printStackTrace();
        } finally {
            reset();
        }
    }

    private void initViewGroupByView(View view) {

        View mCurrentView = view;
        if (mCurrentView != null) {
            mViewGroups.clear();

            ViewGroup tempGroup = (ViewGroup) mCurrentView.getParent();
            if (tempGroup != null) {
                int index = 1;
                mViewGroups.put(index + "", tempGroup);
                print("key:1 " + tempGroup);

                while (tempGroup != null) {
                    tempGroup = (ViewGroup) tempGroup.getParent();
                    mViewGroups.put(++index + "", tempGroup);
                    print(index + ":" + tempGroup);
                }
            } else {
                print("view group of " + mCurrentView + " is null");
            }
        } else {
            Toast.makeText(getContext(), "mCurrentView is null ", Toast.LENGTH_LONG).show();
        }
    }

    private View initCurrentViewByCurrentFocusView() {
        if (mContext.get() instanceof Activity) {
            mCurrentView = ((Activity) mContext.get()).getCurrentFocus();
            return mCurrentView;
        } else {
            Toast.makeText(getContext(), "context not activity", Toast.LENGTH_LONG).show();
            return null;
        }
    }


    private FragmentManager fragmentManager() {
        if (getContext() instanceof AppCompatActivity) {
            return ((AppCompatActivity) getContext()).getSupportFragmentManager();
        } else if (getContext() instanceof Activity) {
//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//                return ((Activity) getContext()).getFragmentManager();
//            }
        }
        return null;
    }

    private void reset() {
        cmdIndex = 0;
    }

    private void dealShowColor() {
        String idStr;
        while ((idStr = nextCmdValue()) != null) {
            int id = mContext.get().getResources().getIdentifier(idStr, "id", mContext.get().getPackageName());
            ((Activity) mContext.get()).findViewById(id).setBackgroundColor(getRandomColor());
        }
    }


    public void print(String str) {
        try {
            if(str == null){
                str = "null";
            }
            mWrite.append(str + "\n").flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void showHelp(final PrintWriter writer) {
        writer.append("Input Error \n").flush();
        for (CmdType cmd : CmdType.values()) {
            writer.append(cmd.toString() + "\n");
        }
        writer.flush();
    }

    private void showHelp(final PrintWriter writer, Collection<Object> obj) {
        writer.append("Input Error \n").flush();
        Collections.showCollection(obj, new Collections.OnShowCollectionHelper<Object>() {
            @Override
            public boolean onCloseInternalPrint() {
                return true;
            }

            @Override
            public void onItem(Object e) {
                writer.append(e.toString() + "\n");
            }

        });
        writer.flush();
    }

    public static String findRDeclaredNameByValue(int value) {
        Class<?> allClassInR[] = R.class.getDeclaredClasses();
        try {
            for (Class<?> oneClass : allClassInR) {
                Field[] field = oneClass.getDeclaredFields();
                for (Field idField : field) {
                    idField.setAccessible(true);
                    int tempValue = 0;
                    try {
                        tempValue = (Integer) idField.get(oneClass);
                    } catch (Exception e) {
                    }
                    if (tempValue == value) {
                        return oneClass.getName() + "." + idField.getName();
                    }
                }
            }
        } catch (IllegalArgumentException e) {

        }
        return "NULL";
    }

    public void updateInputCmd(String[] args) {
        allInputCmd = args;
    }

    private Observable externalCallbacks = new Observable(){
        @Override
        public boolean hasChanged() {
            return true;
        }
    };

    public void addCadConsumer(Observer<DebugUtil> observer) {
        externalCallbacks.addObserver(observer);
    }

    private enum CmdType {
        getVersion,
        getMemoryInfo,

        custom,
        debug_switch,
        save_value_put,
        save_value_get,

        save_value_remove,

        show_fragmentInfo,
        hideIME,
        changeBrand,
        isIMEShow,
        debug_channel_getChannelValue,
        debug_dtv_getRFChannel,
        debug_bluetooth,

        debug_clear_fota,
        fragment_execute_current,
        fragment_list_all,

        execute_update,
        showLogcat,
        window_show_dim,
        window_set_dim,

        show_color_view,
        init_view_group,
        getCurrentFocusView,
        getFocusViewNextFocusId,
        debugViewHierarchy,
        debug_view_all,
        debug_view_pick,

        test
    }

}
